package Principal;

import java.util.ArrayList;
//import java.util.Date;
import java.util.List;
import java.util.Random;
import java.lang.Math;
import java.math.BigDecimal;

public class Host {

	public enum DistribuicaoIntervalo {DETERMINISTICO, EXPONENCIAL}
	
	private BigDecimal intervaloMedio; // Intervalo medio entre chegadas
	private int quadrosMsg; // Numero de quadros por mensagem
	private DistribuicaoIntervalo distIntervalo; // Distribuicao do intervalo (D ou E)	
	private int numeroChegadas; //Numero de chegadas que o host ja teve ate o momento
	private int numeroMaxChegadas; //Numero maximo de chegadas que o host pode ter
	private BigDecimal atrasoPropagacao; //atraso para enviar um pacote pelo meio fisico
	private int numeroJam;
	private boolean meioLivre;
	private boolean isTransmitindo;
	private BigDecimal tempoUltimaTransmissaoSistema; //E o tempo da ultima Transmissao no sistema
	private BigDecimal tempoUltimaTransmissaoHost; //E o tempo da ultima Transmissao do host
	public BigDecimal tempoUltimoInicioTransmissao;
	private boolean isEscutandoMeio;
	private BinaryBackoff binaryBackoff;
	private boolean isPegandoInicioTransmissao;
	private int numeroQuadrosTxSucesso;
	private List<Mensagem> filaMsgs;
	private boolean retireiFimTx;
	
	
	
	private final BigDecimal TEMPO_ESPERA = new BigDecimal(9.6F);	
	public final BigDecimal TEMPO_ENVIO_PACOTE = new BigDecimal(800F);
	public final BigDecimal TEMPO_JAM = new BigDecimal(3.2F);
	private final int NUMERO_MAXIMO_COLISAO = 16;
	private Random generator;
	private boolean statusMeio;
	private String id;	
	
	public Host(String id, BigDecimal _atrasoPropagacao, BigDecimal _intervaloMedio, int _quadrosMsg, DistribuicaoIntervalo _distIntervalo, int _quantidadeMaxChegadas, int _sementeHost){
		this.id = id;
		intervaloMedio = _intervaloMedio; 
		quadrosMsg = _quadrosMsg;
		distIntervalo = _distIntervalo;		
		atrasoPropagacao = _atrasoPropagacao;
		numeroMaxChegadas = _quantidadeMaxChegadas;
		numeroChegadas = 0;
		meioLivre=true;
		isTransmitindo = false;
		isEscutandoMeio = false;
		generator = new Random(_sementeHost);
		numeroJam = 0;
		tempoUltimaTransmissaoSistema = BigDecimal.ZERO;
		tempoUltimaTransmissaoHost = BigDecimal.ZERO;
		tempoUltimoInicioTransmissao = BigDecimal.ZERO;
		binaryBackoff = new BinaryBackoff(_sementeHost);
		isPegandoInicioTransmissao = false;
		numeroQuadrosTxSucesso = 0;
		filaMsgs = new ArrayList<Mensagem>();
		statusMeio = false;
		retireiFimTx = false;		
	}	
	
	public int getQuadroMsg(){
		return quadrosMsg;
	}
	
	public boolean isAtivo() {
		return intervaloMedio != BigDecimal.ZERO;
	}
	
	/*
	 * Calcula o tempo para a proxima chegada de mensagem no host
	 */
	public BigDecimal calcularProximaChegada(BigDecimal tempo){
		
		if(distIntervalo==DistribuicaoIntervalo.DETERMINISTICO)
		{
			return intervaloMedio.add(tempo);
		}else{
			// Intervalo Exponencial
			
			double numAleatorio = generator.nextDouble();
			BigDecimal intervaloEntreChegadas = new BigDecimal(Math.log(numAleatorio)).divide((new BigDecimal(1).divide(intervaloMedio))).negate();
			return tempo.add(intervaloEntreChegadas) ;
		}
	}
	
	public void adicionaMsgFila(){
		//O tamanho do quadro e sempre deterministico
		boolean primeiraChegada = (numeroChegadas == 1);
		Mensagem novaMsg = new Mensagem(quadrosMsg, primeiraChegada);
		filaMsgs.add(novaMsg);
		
	}
	
	public void removeQuadroFila(){
		filaMsgs.get(0).retiraQuadroMsg();
	}
	
	public void incrementaQuadroTxSucesso(){
		numeroQuadrosTxSucesso++;

		filaMsgs.get(0).resetQuadro();
		
		if(filaMsgs.get(0).isFimMsg()){
			filaMsgs.remove(0);
		}
	}
	
	public boolean IsMeiolivre(){        
        return meioLivre;     
    }

    public void setMeioOcupado(){
        meioLivre = false;
    }
    
    public void setMeioLivre(){
        meioLivre = true;   
    }
	
    public void setTempoUltimaTransmissaoSistema(BigDecimal tempo){
        tempoUltimaTransmissaoSistema = tempo;   
    }
    
    public boolean isTransmitindo(){
    	return isTransmitindo;
    }
    
    public void setTransmitindo(){
    	
        isTransmitindo = true;       
    } 
    
    public void setNaoTransmitindo(){
    	
    	isTransmitindo = false;
    }
    
    /*
     * Retorna quanto tempo o host tem que esperar para transmitir um pacote
     */     
    public BigDecimal getTempoEspera(BigDecimal tempoExecucao){
    	
    	if(tempoExecucao == BigDecimal.ZERO){
    		return BigDecimal.ZERO;
    	}
    	
    	BigDecimal soma = tempoUltimaTransmissaoSistema.add(TEMPO_ESPERA);
    	int maior = tempoExecucao.compareTo(soma);
    	if( maior >= 0 )return BigDecimal.ZERO;
    	
    	return TEMPO_ESPERA.subtract(tempoExecucao.subtract(tempoUltimaTransmissaoSistema));
    }
    

    public void setEscutandoMeio(){
        isEscutandoMeio = true;
    }
    
    public void setNotEscutandoMeio(){
    	isEscutandoMeio = false;
    }
    
    public boolean isEscutandoMeio(){
    	return isEscutandoMeio;
    }
    
    public void incrementaNumeroChegadas() {
    	numeroChegadas++;
    }

	public BigDecimal getAtrasoPropagacao() {
		return atrasoPropagacao;
	}

	public boolean isNumeroMaximoChegadas() {
		return numeroMaxChegadas <= numeroChegadas;
	}
	
	public boolean isFilaVazia(){
		return (filaMsgs.isEmpty());		
	}
	
	public void incrementaJam(){
		numeroJam ++;
	}
    
	public void decrementaJam(){
		if(numeroJam == 0){
			return ;
		}
		numeroJam --;
	}
	
	public boolean isUltimoJam(){
		return (numeroJam == 0);
	}

	public BigDecimal calcularBinaryBackoff() {		
		return binaryBackoff.gerarTempo();
	}
	
	public void incrementarColisao(){
		binaryBackoff.incrementaNumeroColisao();
		filaMsgs.get(0).incrementaNumeroColisoes();
	}
	
	public boolean isDescarteQuadro(){
		return (binaryBackoff.getNumeroColisoes() == NUMERO_MAXIMO_COLISAO);
	}
	
	public void resetBackoff(){
		binaryBackoff.resetBinaryBackoff();
	}

	public void setTempoUltimaTransmissaoHost(BigDecimal tempoUltimaTransmissaoHost) {
		this.tempoUltimaTransmissaoHost = tempoUltimaTransmissaoHost;
	}

	public BigDecimal getTempoUltimaTransmissaoHost() {
		return tempoUltimaTransmissaoHost;
	}

	
	public void setTempoUltimoInicioTransmissao(BigDecimal tempoUltimoInicioTransmissao){
		this.tempoUltimoInicioTransmissao = tempoUltimoInicioTransmissao;
		
	}
	
	public BigDecimal getTempoUltimoInicioTransmissao(){
		return filaMsgs.get(0).getTempoInicioTxQuadro();
	}

	public int getNumeroQuadrosSucesso() {
		return numeroQuadrosTxSucesso;
	}

	public void setInicioTx(BigDecimal tempo) {
		filaMsgs.get(0).IniciaTx(tempo);
	}

	public boolean isFimMsg() {
		return filaMsgs.isEmpty() || filaMsgs.get(0).isFimMsg();
	}

	public BigDecimal getTempoAcessoMsg() {
		return filaMsgs.get(0).getTempoInicioTxMsg();
	}

	public void setInicioTxOcupado(BigDecimal tempoExecucao) {
		filaMsgs.get(0).iniciaTxOcupado(tempoExecucao);
		
	}

	public BigDecimal getTempoAcessoQuadro() {
		return filaMsgs.get(0).getTempoAcessoQuadro();
	}

	public BigDecimal getNcm() {
		return new BigDecimal(filaMsgs.get(0).getNCm());
	}

	public void descartaQuadro() {
		filaMsgs.get(0).descartaQuadro();
		if(filaMsgs.get(0).isFimMsg()){
			filaMsgs.remove(0);
		}
	}

	public boolean jaVerifiqueiMeio() {
		return statusMeio;
	}

	public void setaVerifiqueiMeio() {
		statusMeio = true;
		
	}

	public void resetStatusMeio() {
		statusMeio = false;
		
	}

	public void retiraFimTx() {
		retireiFimTx = true;
	}

	public void coloqueiFimTx() {
		retireiFimTx = false;
		
	}

	public boolean retireiFimTx() {
		return retireiFimTx;
	}
	
	@Override
	public String toString() {
		return id;
	}

	public void resetNumeroQuadrosSucesso() {
		numeroQuadrosTxSucesso = 0;
		
	}



}